package cloud.seri.auth.web.rest

import cloud.seri.auth.repository.UserRepository
import cloud.seri.auth.security.SecurityUtils
import cloud.seri.auth.service.UserService
import cloud.seri.auth.service.dto.PasswordChangeDTO
import cloud.seri.auth.service.dto.UserDTO
import cloud.seri.auth.service.mapper.UserMapper
import cloud.seri.auth.web.rest.errors.EmailAlreadyUsedException
import cloud.seri.auth.web.rest.errors.EmailNotFoundException
import cloud.seri.auth.web.rest.errors.InternalServerErrorException
import cloud.seri.auth.web.rest.errors.InvalidPasswordException
import cloud.seri.auth.web.rest.errors.LoginAlreadyUsedException
import cloud.seri.auth.web.rest.vm.KeyAndPasswordVM
import cloud.seri.auth.web.rest.vm.ManagedUserVM
import io.swagger.annotations.Api
import io.swagger.annotations.ApiOperation
import net.dreamlu.mica.core.result.R

import org.apache.commons.lang3.StringUtils
import org.slf4j.LoggerFactory
import org.springframework.http.HttpStatus
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.ResponseStatus
import org.springframework.web.bind.annotation.RestController

import javax.servlet.http.HttpServletRequest
import javax.validation.Valid

/**
 * REST controller for managing the current user's account.
 */
@RestController
@RequestMapping("/api")
class AccountResource(
    private val userRepository: UserRepository,
    private val userService: UserService,
    private val userMapper: UserMapper
)
{

    private val log = LoggerFactory.getLogger(AccountResource::class.java)

    /**
     * `POST  /register` : register the user.
     *
     * @param managedUserVM the managed user View Model.
     * @throws InvalidPasswordException `400 (Bad Request)` if the password is incorrect.
     * @throws EmailAlreadyUsedException `400 (Bad Request)` if the email is already used.
     * @throws LoginAlreadyUsedException `400 (Bad Request)` if the login is already used.
     */
//    @PostMapping("/register")
//    @ResponseStatus(HttpStatus.CREATED)
//    fun registerAccount(@Valid @RequestBody managedUserVM: ManagedUserVM)
//    {
//        if (!checkPasswordLength(managedUserVM.password))
//        {
//            throw InvalidPasswordException()
//        }
//        val user = userService.registerUser(managedUserVM, managedUserVM.password!!)
//    }

    /**
     * `GET  /activate` : activate the registered user.
     *
     * @param key the activation key.
     * @throws RuntimeException `500 (Internal Server Error)` if the user couldn't be activated.
     */
    @GetMapping("/activate")
    @ApiOperation("激活账号", nickname = "ActivateAccount")
    fun activateAccount(@RequestParam(value = "key") key: String)
    {
        val user = userService.activateRegistration(key)
        if (!user.isPresent)
        {
            throw InternalServerErrorException("No user was found for this activation key")
        }
    }

    /**
     * `GET  /authenticate` : check if the user is authenticated, and return its login.
     *
     * @param request the HTTP request.
     * @return the login if the user is authenticated.
     */
    @GetMapping("/authenticate")
    @ApiOperation("是否授权", nickname = "IsAuthenticated")
    fun isAuthenticated(request: HttpServletRequest): String?
    {
        log.debug("REST request to check if the current user is authenticated")
        return request.remoteUser
    }

    /**
     * `GET  /account` : get the current user.
     *
     * @return the current user.
     * @throws RuntimeException `500 (Internal Server Error)` if the user couldn't be returned.
     */
    @GetMapping("/account")
    @ApiOperation("获取用户信息", nickname = "GetAccount")
    fun getAccount(): UserDTO
    {
        return userService.getUserWithAuthorities()
            .map { userMapper.toDto(it) }
            .orElseThrow { InternalServerErrorException("User could not be found") }
    }

    /**
     * POST  /account : update the current user information.
     *
     * @param userDTO the current user information
     * @throws EmailAlreadyUsedException `400 (Bad Request)` if the email is already used.
     * @throws RuntimeException `500 (Internal Server Error)` if the user login wasn't found.
     */
    @PostMapping("/account")
    @ApiOperation("保存用户信息", nickname = "SaveAccount")
    fun saveAccount(@Valid @RequestBody userDTO: UserDTO): R<Void>
    {
        val userLogin = SecurityUtils.getCurrentUserLogin().orElseThrow { InternalServerErrorException("Current user login not found") }
        val existingUser = userRepository.findOneByEmailIgnoreCase(userDTO.email)
        if (existingUser.isPresent && !existingUser.get().login.equals(userLogin, ignoreCase = true))
        {
            return R.fail("邮箱已经存在")
        }
        val user = userRepository.findOneByLogin(userLogin)
        if (!user.isPresent)
        {
            return R.fail("用户没找到")
        }
        userService.updateUser(
            userDTO.userName, userDTO.realName, userDTO.email,
            userDTO.langKey, userDTO.imageUrl
        )
        return R.success()
    }

    /**
     * POST  /account/change-password : changes the current user's password.
     *
     * @param passwordChangeDto current and new password.
     * @throws InvalidPasswordException `400 (Bad Request)` if the new password is incorrect.
     */
    @PostMapping(path = ["/account/change-password"])
    @ApiOperation("修改密码", nickname = "ChangePassword")
    fun changePassword(@RequestBody passwordChangeDto: PasswordChangeDTO): R<Void>
    {
        if (!checkPasswordLength(passwordChangeDto.newPassword))
        {
            throw InvalidPasswordException()
        }
        userService.changePassword(passwordChangeDto.currentPassword!!, passwordChangeDto.newPassword!!)
        return R.success()
    }

    /**
     * POST   /account/reset-password/init : Send an email to reset the password of the user
     *
     * @param mail the mail of the user
     * @throws EmailNotFoundException `400 (Bad Request)` if the email address is not registered
     */
//    @PostMapping(path = ["/account/reset-password/init"])
//    fun requestPasswordReset(@RequestBody mail: String)
//    {
////        mailService.sendPasswordResetMail(
////            userService.requestPasswordReset(mail)
////                .orElseThrow<EmailNotFoundException> { EmailNotFoundException() }
////        )
//    }

    /**
     * `POST   /account/reset-password/finish` : Finish to reset the password of the user.
     *
     * @param keyAndPassword the generated key and the new password.
     * @throws InvalidPasswordException `400 (Bad Request)` if the password is incorrect.
     * @throws RuntimeException `500 (Internal Server Error)` if the password could not be reset.
     */
    @PostMapping(path = ["/account/reset-password/finish"])
    @ApiOperation("重置密码", nickname = "PasswordResetFinish")
    fun finishPasswordReset(@RequestBody keyAndPassword: KeyAndPasswordVM): R<Void>
    {
        if (!checkPasswordLength(keyAndPassword.newPassword))
        {
            throw InvalidPasswordException()
        }
        val user = userService.completePasswordReset(keyAndPassword.newPassword!!, keyAndPassword.key!!)

        if (!user.isPresent)
        {
            return R.fail("用户没找到")
        }
        return R.success()
    }

    companion object
    {

        @JvmStatic
        private fun checkPasswordLength(password: String?): Boolean
        {
            return !StringUtils.isEmpty(password) &&
                password!!.length >= ManagedUserVM.PASSWORD_MIN_LENGTH &&
                password.length <= ManagedUserVM.PASSWORD_MAX_LENGTH
        }
    }
}
